use std::future::Future;
use std::ops::Deref;
use std::pin::{pin, Pin};
use std::time::Duration;
use trpl::{Either, Html, ReceiverStream, Stream};

fn main() {
    let args: Vec<String> = std::env::args().collect();

    trpl::run(async {
        let title_fut_1 = page_title(&args[1]);
        let title_fut_2 = page_title(&args[2]);

        let (url, maybe_title) =
            match trpl::race(title_fut_1, title_fut_2).await {
                Either::Left(left) => left,
                Either::Right(right) => right,
            };

        println!("{url} returned first");
        match maybe_title {
            Some(title) => println!("Its page title is: '{title}'"),
            None => println!("Its title could not be parsed."),
        }
    });

    trpl::run(async {
        let fut1 = async {
            for i in 1..10 {
                println!("hi number {i} from the first task!");
                trpl::sleep(Duration::from_millis(500)).await;
            }
        };

        let fut2 = async {
            for i in 1..5 {
                println!("hi number {i} from the second task!");
                trpl::sleep(Duration::from_millis(500)).await;
            }
        };

       trpl::join(fut1, fut2).await;
    });

    trpl::run(async {
        let (tx, mut rx) = trpl::channel();

        let val = String::from("hi");
        tx.send(val).unwrap();

        let received = rx.recv().await.unwrap();
        println!("Got: {received}");
    });

    trpl::run(async {
        let (tx, mut rx) = trpl::channel();

        let fut1 = pin!(async move {
            let vals = vec![
                String::from("hi"),
                String::from("from"),
                String::from("the"),
                String::from("future"),
            ];

            for val in vals {
                tx.send(val).unwrap();
                println!("send");
                trpl::yield_now().await;
            }
        });

        let fut2 = pin!(async {
            while let Some(value) = rx.recv().await {
                println!("received '{value}'");
            }
        });
        let futures:Vec<Pin<&mut dyn Future<Output = ()>>> = vec![fut1, fut2];
        trpl::join_all(futures).await;
    });
    use trpl::StreamExt;
    trpl::run(async {
        let values = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
        let iter = values.iter().map(|n| n * 2);
        let mut stream = trpl::stream_from_iter(iter);
        let mut filtered =
            stream.filter(|value| value % 3 == 0 || value % 5 == 0);

        while let Some(value) = filtered.next().await {
            println!("The value was: {value}");
        }
    });

    fn get_messages() -> impl Stream<Item = String> {
        let (tx, rx) = trpl::channel();

        let messages = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j"];
        for message in messages {
            tx.send(format!("Message: '{message}'")).unwrap();
        }

        ReceiverStream::new(rx)
    }

    trpl::run(async {
        let mut messages = get_messages();

        while let Some(message) = messages.next().await {
            println!("{message}");
        }
    });

    let (mut a,  b , c) = (1, 2, 3);
    fn print_coordinates((mut x, y): (i32, i32)) {
        println!("Current location: ({x}, {y})");
    }
    print_coordinates((1,2));
    struct Point { x : i32, y : i32, z:i32 };
    let mut p = Point { x : 1, y : 2,z:3 };
    let Point {  x , z, .. } = &p;
    println!("Current location: ({x}, {z})");
    p.x = 2;

    let mut num = 5;

    let r2 = &mut num as *mut i32;
    let r1 = &num as *const i32;
    let address = 0x012345usize;
    let r = address as *const i32;
    unsafe fn dangerous() {}

    unsafe {
        dangerous();
    }

    let mut v = vec![1, 2, 3, 4, 5, 6];

    let r = &mut v[..];

    let (a, b) = r.split_at_mut(3);

    assert_eq!(a, &mut [1, 2, 3]);
    assert_eq!(b, &mut [4, 5, 6]);

    extern "C" {
        fn abs(input: i32) -> i32;
    }
    unsafe {
        println!("Absolute value of -3 according to C: {}", abs(-3));
    }
}

async fn page_title(url: &str) -> (&str, Option<String>) {
    let text = trpl::get(url).await.text().await;
    let title = Html::parse(&text)
        .select_first("title")
        .map(|title| title.inner_html());
    (url, title)
}




